#include "FreeRTOS.h"
#include "task.h"

#include "infrastructure.h"

#include "Controller.h"
#include "DisplayManager.h"
#include "KeypadManager.h"
#include "ConsoleManager.h"
#include "HAL.h"
#include "TemperatureMonitor.h"

/* PROTECTED REGION ID(Controller_customCode) ENABLED START */

#include "hw_memmap.h"
#include "hw_types.h"
#include "hw_sysctl.h"
#include "hw_ints.h"
#include "sysctl.h"
#include "gpio.h"
#include "adc.h"
#include "timer.h"
#include "pwm.h"
#include "interrupt.h"
#include "uartstdio.h"

#include "drivers/rit128x96x4.h"

#define F_PWM			500
#define F_S				50

/* PROTECTED REGION END */

static void setupHardware(void) {
	/* PROTECTED REGION ID(Controller_setupHardwareFunc) ENABLED START */

	/*
	 * If running on Rev A2 silicon, turn the LDO voltage up to 2.75V.
	 * This is a workaround to allow the PLL to operate reliably.
	 */
	if (DEVICE_IS_REVA2) {
		SysCtlLDOSet( SYSCTL_LDO_2_75V);
	}

	/* Set the clocking to run from the PLL at 50 MHz */
	SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ);

	/* Setup buttons */
	SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOE);
	GPIOPinTypeGPIOInput(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	GPIOPadConfigSet(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
	GPIOIntTypeSet(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_FALLING_EDGE);
	GPIOPinIntEnable(GPIO_PORTE_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	IntPrioritySet(INT_GPIOE, configKERNEL_INTERRUPT_PRIORITY);
	IntEnable( INT_GPIOE);

	/* Setup Timer for debouncing buttons */
	SysCtlPeripheralEnable( SYSCTL_PERIPH_TIMER1);
	TimerConfigure(TIMER1_BASE, TIMER_CFG_32_BIT_OS);
	TimerLoadSet(TIMER1_BASE, TIMER_A, 0);
	TimerEnable(TIMER1_BASE, TIMER_A);

	/* Setup ADC Timer */
	SysCtlPeripheralEnable( SYSCTL_PERIPH_TIMER0);
	TimerConfigure(TIMER0_BASE, TIMER_CFG_32_BIT_PER);
	TimerControlTrigger(TIMER0_BASE, TIMER_A, true);
	TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet() / F_S);
	TimerEnable(TIMER0_BASE, TIMER_A);

	/* Setup ADC */
	SysCtlPeripheralEnable( SYSCTL_PERIPH_ADC0);
	ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0);
	ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH0);
	ADCIntEnable(ADC0_BASE, 0);
	IntPrioritySet(INT_ADC0, configKERNEL_INTERRUPT_PRIORITY);
	IntEnable( INT_ADC0);
	ADCSequenceEnable(ADC0_BASE, 0);

	/* Setup PWM */
	SysCtlPWMClockSet( SYSCTL_PWMDIV_1);
	SysCtlPeripheralEnable( SYSCTL_PERIPH_PWM);
	SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOB);
	GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_0);
	PWMGenConfigure(PWM_BASE, PWM_GEN_1, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
	PWMGenPeriodSet(PWM_BASE, PWM_GEN_1, SysCtlClockGet() / F_PWM);
	PWMPulseWidthSet(PWM_BASE, PWM_OUT_2, 0);
	PWMOutputState(PWM_BASE, PWM_OUT_2_BIT, true);
	PWMGenEnable(PWM_BASE, PWM_GEN_1);

	/* Setup display */
	RIT128x96x4Init(1000000);

	/* Setup UART */
	SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOA);
	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	UARTStdioInit(0);

	/* PROTECTED REGION END */
}

int main(void) {
	setupHardware();
	initializeInfrastructure();

	xTaskCreate(Controller_Task, (signed portCHAR *) "Controller", configMINIMAL_STACK_SIZE, NULL, 5, NULL);
	xTaskCreate(DisplayManager_Task, (signed portCHAR *) "DisplayManager", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
	xTaskCreate(KeypadManager_Task, (signed portCHAR *) "KeypadManager", configMINIMAL_STACK_SIZE, NULL, 3, NULL);
	xTaskCreate(ConsoleManager_Task, (signed portCHAR *) "ConsoleManager", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
	xTaskCreate(HAL_Task, (signed portCHAR *) "HAL", configMINIMAL_STACK_SIZE, NULL, 6, NULL);
	xTaskCreate(TemperatureMonitor_Task, (signed portCHAR *) "TemperatureMonitor", configMINIMAL_STACK_SIZE, NULL, 4, NULL);

	vTaskStartScheduler();

	return 0;
}
